﻿import os
import shutil
from datetime import datetime
from typing import Optional, List

import uvicorn
from fastapi import FastAPI, File, UploadFile, HTTPException, Form, Request
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from werkzeug.utils import secure_filename

# **创建FastAPI应用实例**
app = FastAPI(title="File Management API", version="1.0.0")

# **配置CORS中间件**
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源
    allow_credentials=True,
    allow_methods=["*"],  # 允许所有HTTP方法
    allow_headers=["*"],  # 允许所有请求头
)

# **配置常量**
UPLOAD_FOLDER = 'uploads'
MAX_CONTENT_LENGTH = 100 * 1024 * 1024  # 100MB max file size

# **确保上传目录存在**
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# **定义Pydantic模型**
class DirectoryCreate(BaseModel):
    path: str = ""
    name: str

class FileCreate(BaseModel):
    path: str = ""
    filename: str
    content: str = ""

class DeleteRequest(BaseModel):
    path: str

def get_file_info(filepath: str) -> dict:
    """**获取文件信息**"""
    stat = os.stat(filepath)
    return {
        'size': stat.st_size,
        'modified': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
        'created': datetime.fromtimestamp(stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
    }

@app.get('/api/directories')
async def list_directories(path: str = ""):
    """**获取目录结构**"""
    try:
        full_path = os.path.join(UPLOAD_FOLDER, path)

        if not os.path.exists(full_path):
            raise HTTPException(status_code=404, detail='Directory not found')

        items = []
        for item in os.listdir(full_path):
            item_path = os.path.join(full_path, item)
            relative_path = os.path.join(path, item).replace('\\', '/')

            if os.path.isdir(item_path):
                items.append({
                    'name': item,
                    'type': 'directory',
                    'path': relative_path,
                    **get_file_info(item_path)
                })
            else:
                items.append({
                    'name': item,
                    'type': 'file',
                    'path': relative_path,
                    **get_file_info(item_path)
                })

        # **排序：目录在前，文件在后**
        items.sort(key=lambda x: (x['type'] == 'file', x['name'].lower()))

        return {
            'current_path': path,
            'items': items
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post('/api/directories')
async def create_directory(directory_data: DirectoryCreate):
    """**创建目录**"""
    try:
        path = directory_data.path
        dir_name = directory_data.name

        if not dir_name:
            raise HTTPException(status_code=400, detail='Directory name is required')

        # **安全文件名处理**
        dir_name = secure_filename(dir_name)
        if not dir_name:
            raise HTTPException(status_code=400, detail='Invalid directory name')

        full_path = os.path.join(UPLOAD_FOLDER, path, dir_name)

        if os.path.exists(full_path):
            raise HTTPException(status_code=400, detail='Directory already exists')

        os.makedirs(full_path)
        return {'message': 'Directory created successfully'}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post('/api/upload')
async def upload_file(file: UploadFile = File(...), path: str = Form("")):
    """**上传文件**"""
    try:
        if not file.filename:
            raise HTTPException(status_code=400, detail='No file selected')

        # **检查文件大小**
        file_content = await file.read()
        if len(file_content) > MAX_CONTENT_LENGTH:
            raise HTTPException(status_code=413, detail='File too large')

        # **安全文件名处理**
        filename = secure_filename(file.filename)
        if not filename:
            raise HTTPException(status_code=400, detail='Invalid filename')

        # **确保目标目录存在**
        target_dir = os.path.join(UPLOAD_FOLDER, path)
        if not os.path.exists(target_dir):
            os.makedirs(target_dir)

        file_path = os.path.join(target_dir, filename)

        # **如果文件已存在，添加数字后缀**
        counter = 1
        original_filename = filename
        name, ext = os.path.splitext(original_filename)

        while os.path.exists(file_path):
            filename = f"{name}_{counter}{ext}"
            file_path = os.path.join(target_dir, filename)
            counter += 1

        # **保存文件**
        with open(file_path, 'wb') as f:
            f.write(file_content)

        return {
            'message': 'File uploaded successfully',
            'filename': filename
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post('/api/files')
async def create_file(file_data: FileCreate):
    """**创建新文件**"""
    try:
        path = file_data.path
        filename = file_data.filename
        content = file_data.content

        if not filename:
            raise HTTPException(status_code=400, detail='Filename is required')

        # **安全文件名处理**
        filename = secure_filename(filename)
        if not filename:
            raise HTTPException(status_code=400, detail='Invalid filename')

        # **确保目标目录存在**
        target_dir = os.path.join(UPLOAD_FOLDER, path)
        if not os.path.exists(target_dir):
            os.makedirs(target_dir)

        file_path = os.path.join(target_dir, filename)

        if os.path.exists(file_path):
            raise HTTPException(status_code=400, detail='File already exists')

        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)

        return {'message': 'File created successfully'}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get('/api/download/{filepath:path}')
async def download_file(filepath: str):
    """**下载文件**"""
    try:
        file_path = os.path.join(UPLOAD_FOLDER, filepath)

        if not os.path.exists(file_path) or os.path.isdir(file_path):
            raise HTTPException(status_code=404, detail='File not found')

        return FileResponse(
            path=file_path,
            filename=os.path.basename(file_path),
            media_type='application/octet-stream'
        )
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.delete('/api/delete')
async def delete_item(delete_data: DeleteRequest):
    """**删除文件或目录**"""
    try:
        path = delete_data.path

        if not path:
            raise HTTPException(status_code=400, detail='Path is required')

        full_path = os.path.join(UPLOAD_FOLDER, path)

        if not os.path.exists(full_path):
            raise HTTPException(status_code=404, detail='File or directory not found')

        if os.path.isdir(full_path):
            shutil.rmtree(full_path)
            return {'message': 'Directory deleted successfully'}
        else:
            os.remove(full_path)
            return {'message': 'File deleted successfully'}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get('/api/file-content/{filepath:path}')
async def get_file_content(filepath: str):
    """**获取文件内容（用于预览文本文件）**"""
    try:
        file_path = os.path.join(UPLOAD_FOLDER, filepath)

        if not os.path.exists(file_path) or os.path.isdir(file_path):
            raise HTTPException(status_code=404, detail='File not found')

        # **只读取文本文件**
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            return {'content': content}
        except UnicodeDecodeError:
            raise HTTPException(status_code=400, detail='Cannot preview binary file')
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get('/')
async def root():
    """**根路径**"""
    return {"message": "File Management API is running"}

@app.post('/api/upload-folder')
async def upload_folder(
    files: List[UploadFile] = File(...),
    paths: List[str] = Form(...),
    basePath: str = Form(default='')
):
    """**上传文件夹到指定目录**"""
    try:
        if not files:
            raise HTTPException(status_code=400, detail="No files provided")

        if len(files) != len(paths):
            raise HTTPException(status_code=400, detail="Files and paths count mismatch")

        uploaded_files = []
        created_dirs = set()

        for file, relative_path in zip(files, paths):
            if not file.filename:
                continue

            # 读取文件内容
            content = await file.read()
            if len(content) > MAX_CONTENT_LENGTH:
                continue  # 跳过过大的文件

            # 处理相对路径 - 这里是关键修改
            # relative_path 包含了完整的文件夹结构，如: "folder1/subfolder/file.txt"
            # 我们需要保持这个结构，并将其放置到 basePath 指定的目录下

            # 构建完整的目标路径
            # basePath 是用户当前所在的目录
            # relative_path 是文件在上传文件夹中的相对路径
            if basePath:
                full_relative_path = os.path.join(basePath, relative_path).replace('\\', '/')
            else:
                full_relative_path = relative_path.replace('\\', '/')

            target_path = os.path.join(UPLOAD_FOLDER, full_relative_path)

            # 确保目录存在
            target_dir = os.path.dirname(target_path)
            if target_dir not in created_dirs and not os.path.exists(target_dir):
                os.makedirs(target_dir, exist_ok=True)
                created_dirs.add(target_dir)

            # 处理文件名冲突
            original_target = target_path
            counter = 1
            name, ext = os.path.splitext(os.path.basename(target_path))

            while os.path.exists(target_path):
                new_name = f"{name}_{counter}{ext}"
                target_path = os.path.join(os.path.dirname(original_target), new_name)
                counter += 1

            # 保存文件
            with open(target_path, "wb") as buffer:
                buffer.write(content)

            uploaded_files.append({
                'original_path': relative_path,
                'saved_path': os.path.relpath(target_path, UPLOAD_FOLDER).replace('\\', '/'),
                'filename': os.path.basename(target_path)
            })

        return JSONResponse(
            status_code=201,
            content={
                'message': f'Successfully uploaded {len(uploaded_files)} files to {basePath or "root directory"}',
                'uploaded_files': uploaded_files,
                'created_directories': len(created_dirs),
                'target_directory': basePath
            }
        )

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


if __name__ == '__main__':
    print("**🚀 Backend server starting on http://localhost:8080**")
    uvicorn.run("app:app", host="0.0.0.0", port=8080, reload=True)
